#include <stdarg.h>
#include <string.h>
#include <stdio.h>

// See https://code.google.com/p/gfb/ for libraries
// Some patches needed, see README.txt
#include <OneWire.h>
#include <DallasTemperature.h>
#include <DS2409.h>
#include <DS2438.h>
#include <DS2423.h>

#include <Ethernet.h>
#include <Server.h>
#include <Client.h>

#include <LiquidCrystal.h>

#define LCD_REFRESH        2000
#define SERIAL_REFRESH     30000
#define COUNTER_REFRESH    30000
#define MIN_1              60 * 1000
#define MIN_5              1_MIN * 5
#define MIN_15             1_MIN * 15
#define MIN_30             1_MIN * 30

#define SHARE_FEED_ID              xxxx     // this is your Pachube feed ID that you want to share to
#define REMOTE_FEED_ID             256      // this is the ID of the remote Pachube feed that you want to connect to
#define REMOTE_FEED_DATASTREAMS    4        // make sure that remoteSensor array is big enough to fit all the remote data streams
#define UPDATE_INTERVAL            30000    // if the connection is good wait 10 seconds before updating again - should not be less than 5
#define RESET_INTERVAL             30000    // if connection fails/resets wait 10 seconds before trying again - should not be less than 5

#define PACHUBE_API_KEY            "" // fill in your API key


/*** 1-wire Networking ***/

// 1-wire device addresses
DeviceAddress sw1 = { 0x1f, 0xb2, 0xe2, 0x03, 0x00, 0x00, 0x00, 0x01 };
DeviceAddress sw2 = { 0x1f, 0xbe, 0xe9, 0x03, 0x00, 0x00, 0x00, 0x0c };
DeviceAddress sw3 = { 0x1f, 0x0f, 0xe1, 0x03, 0x00, 0x00, 0x00, 0x02 };

DeviceAddress hum1 = { 0x26, 0xa5, 0x65, 0xb7, 0x00, 0x00, 0x00, 0x02 };
DeviceAddress count1 = { 0x1d, 0x4c, 0x8d, 0x0c, 0x00, 0x00, 0x00, 0xfe };

DeviceAddress t1p1 = { 0x10, 0x83, 0x1e, 0x56, 0x01, 0x08, 0x00, 0x9a };
DeviceAddress t2p3 = { 0x10, 0x72, 0x35, 0x8a, 0x01, 0x08, 0x00, 0x44 };

// Setup oneWire network
OneWire ow(7);

// DS1820 temperature sensors
DallasTemperature sensors(&ow);

// DS2409 switch
ds2409 owSwitch(&ow, sw3, sw2, sw1);

// DS2423 counter
ds2423 counter(&ow, count1);

// DS2438 humidistat
ds2438 humSensor(&ow, hum1);

// Sensor data storage
float t1p1Temp, t2p3Temp, hum1Temp, hum1RH;
unsigned long count1Val, oldCount1Val;
int count1Diff;
unsigned int hum1Solar;
//unsigned int counterData[40];


/*** Ethernet / Networking ***/

// Setup Ethernet shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 2, 209 };
byte remoteServer[] = { 209, 40, 205, 190 };

// Webserver
Server httpd(80);


/*** LCD ***/

// Initialize LCD library
LiquidCrystal lcd(9, 8, 5, 4, 3, 2);

// String LCD buffer
char lcdLine1[20];
char lcdLine2[20];

/*byte newChar[8] = {
	B01100,
	B10010,
	B10010,
	B01100,
	B00000,
	B00000,
	B00000,
	B00000
};*/


// Timing stuff
unsigned long lastLCDMillis = 0;
unsigned long lastSerialMillis = 0;
unsigned long lastCounterMillis = 0;
unsigned long lastCount1min = 0;
unsigned long lastCount5min = 0;
unsigned long lastCount15min = 0;
unsigned long lastCount30min = 0;
unsigned long lastValue1min = 0;
unsigned long lastValue5min = 0;
unsigned long lastValue15min = 0;
unsigned long lastValue30min = 0;



Client localClient(remoteServer, 80);
unsigned int interval;

char pachube_data[70];

boolean found_status_200 = false;
boolean found_session_id = false;
boolean found_CSV = false;
char *found;
unsigned int successes = 0;
unsigned int failures = 0;
boolean ready_to_update = true;
boolean reading_pachube = false;

boolean request_pause = false;
boolean found_content = false;

unsigned long last_connect;

int content_length;

char buff[64];
int pointer = 0;


void p(char *fmt, ... )
{
        char tmp[128]; // resulting string limited to 128 chars
        va_list args;
        va_start (args, fmt );
        vsnprintf(tmp, 128, fmt, args);
        va_end (args);
        Serial.print(tmp);
}

void setup(void)
{
  // Setup
  Serial.begin(9600);

  // Sensors
  sensors.begin();
  humSensor.writeSetup(0x00);
  //oldCount1Val = newCount1Val = 0;

  // Network
  setupEthernet();
  //Ethernet.begin(mac, ip);
  //httpd.begin();

  // LCD
  lcd.begin(16, 2);
  //lcd.createChar(0, newChar);
  
  sensors.begin();
  owSwitch.swCmd(sw1, 0);
  owSwitch.swCmd(sw2, 0);
  owSwitch.swCmd(sw3, 0);

  updateSensors();
  updateLCD();
  updateSerialOut();
  
  oldCount1Val = count1Val;
}

void loop(void)
{
  /*for(int i=1; i<5; i++)
  {
    delay(250);
    updateSensors();
    updateLCD();
  }*/
  //runHttpDaemon();
  pachube_in_out();
  
  if (cycleCheck(&lastLCDMillis, LCD_REFRESH)) {
    updateSensors();
    updateLCD();
  }
  if (cycleCheck(&lastSerialMillis, SERIAL_REFRESH)) {
    updateSensors();
    updateSerialOut();
  }
  /*if (cycleCheck(&lastCounterMillis, COUNTER_REFRESH)) {
    updateCounter();
  }*/
//  if (cycleCheck(&lastCounter1min, 1_MIN)) {  
}

void updateSensors(void)
{
  owSwitch.port(1);
  sensors.setResolution(t1p1, 9);
  delay(250);
  sensors.requestTemperaturesByAddress(t1p1);
  t1p1Temp = sensors.getTempF(t1p1);
  
  owSwitch.port(3);
  sensors.setResolution(t2p3, 9);
  delay(250);
  sensors.requestTemperaturesByAddress(t2p3);
  t2p3Temp = sensors.getTempF(t2p3);
  hum1Temp = humSensor.readTempF();
  hum1RH = humSensor.readHum();
  hum1Solar = humSensor.readCurrent();
  
  owSwitch.port(5);
  count1Val = counter.readCounter(1);
}

void updateLCD(void)
{
  snprintf(lcdLine1, 17, "%dF %dF S:%d   ", int(t1p1Temp), int(t2p3Temp), hum1Solar);
  //snprintf(lcdLine2, 17, "%d%% %d/%d/%d/%d  ", int(hum1RH), count1Val);
  snprintf(lcdLine2, 17, "%d%% %d       ", int(hum1RH), count1Diff, count1Val);

  lcd.setCursor(0, 0);
  lcd.print(lcdLine1);
  lcd.setCursor(0, 1);
  lcd.print(lcdLine2);
}

void updateSerialOut(void)
{
  Serial.print("T1: ");
  Serial.print(t1p1Temp);
  Serial.print("F  T2: ");
  Serial.print(t2p3Temp);
  Serial.println("F");

  Serial.print("Temp: ");
  Serial.print(hum1Temp);
  Serial.print("F  Hum: ");
  Serial.print(hum1RH);
  Serial.print("% RH  Solar: ");
  Serial.print(hum1Solar);
  Serial.println();
  
  p("Counter(diff/abs): %d/%d\n\n", count1Diff, int(count1Val));
}

void httpdOutputData(Client client)
{
  client.print("T1:");
  client.print(t1p1Temp);
  client.println("<br>");
  client.print("T2:");
  client.print(t2p3Temp);
  client.println("<br>");

  client.print("HT1:");
  client.print(hum1Temp);
  client.println("<br>");
  client.print("HUM:");
  client.print(hum1RH);
  client.println("<br>");

  client.print("SOLAR:");
  client.print(hum1Solar);
  client.println("<br>");

  client.print("COUNTERDIFF:");
  client.print(count1Diff);
  client.println("<br>");

  client.print("COUNTERABS:");
  client.print(count1Val);
  client.println("<br>");
}

boolean cycleCheck(unsigned long *lastMillis, unsigned int cycle)
{
  unsigned long currentMillis = millis();
  if (currentMillis - *lastMillis >= cycle) {
    *lastMillis = currentMillis;
    return true;
  }
  else
    return false;
}

void runHttpDaemon(void)
{
  Client client = httpd.available();
  if (client)
  {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        if (c == '\n' && current_line_is_blank)
        {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          updateSensors();
               
          httpdOutputData(client);
          //WebOutputDebug(client);
          
          //got the data, might as well use it...
          updateLCD();
     
          break;
        }
        if (c == '\n') {
          // we're starting a new line
          current_line_is_blank = true;
        } else if (c != '\r') {
          // we've gotten a character on the current line
          current_line_is_blank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}


void pachube_in_out()
{
  if (millis() < last_connect) last_connect = millis();

  if (request_pause){
    if ((millis() - last_connect) > interval){
      ready_to_update = true;
      reading_pachube = false;
      request_pause = false;
      found_status_200 = false;
      found_session_id = false;
      found_CSV = false;

      Serial.print("Ready to connect: ");
      Serial.println(millis());
    }
  }

  if (ready_to_update)
  {
    Serial.println("Connecting...");
    if (localClient.connect())
    {
      // here we assign comma-separated values to 'data', which will update Pachube datastreams
      // we use all the analog-in values, but could of course use anything else millis(), digital
      // inputs, etc. . i also like to keep track of successful and failed connection
      // attempts, sometimes useful for determining whether there are major problems.
      
      count1Diff = count1Val - oldCount1Val;
      oldCount1Val = count1Val;

      //sprintf(pachube_data,"%d,%d,%d,%d,%d,%d,%d,%d",analogRead(0),analogRead(1),analogRead(2),analogRead(3),analogRead(4),analogRead(5), successes + 1, failures);
      sprintf(pachube_data,"%d,%d,%d,%d,%d", int(t1p1Temp), int(hum1RH), count1Diff, successes + 1, failures);
      content_length = strlen(pachube_data);

      Serial.println("GET request to retrieve");

      localClient.print("GET /api/");
      localClient.print(REMOTE_FEED_ID);
      localClient.print(".csv HTTP/1.1\nHost: pachube.com\nX-PachubeApiKey: ");
      localClient.print(PACHUBE_API_KEY);
      localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
      localClient.println("\n");

      Serial.println("finished GET now PUT, to update");
      Serial.println(pachube_data);

      localClient.print("PUT /api/");
      localClient.print(SHARE_FEED_ID);
      localClient.print(".csv HTTP/1.1\nHost: pachube.com\nX-PachubeApiKey: ");
      localClient.print(PACHUBE_API_KEY);

      localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
      localClient.print("\nContent-Type: text/csv\nContent-Length: ");
      localClient.print(content_length);
      localClient.print("\nConnection: close\n\n");
      localClient.print(pachube_data);
      localClient.print("\n");

      /*ready_to_update = false;
      reading_pachube = true;
      request_pause = false;*/
      interval = UPDATE_INTERVAL;

      Serial.print("finished PUT: ");
      Serial.println(millis());

      ready_to_update = false;
      reading_pachube = false;
      request_pause = true;
      last_connect = millis();
      found_content = false;
      localClient.stop();
    } 
    else {
      Serial.print("connection failed!");
      Serial.print(++failures);
      found_status_200 = false;
      found_session_id = false;
      found_CSV = false;
      ready_to_update = false;
      reading_pachube = false;
      request_pause = true;
      last_connect = millis();
      interval = RESET_INTERVAL;
      setupEthernet();
    }
  }

  /*while (reading_pachube){
    while (localClient.available()) {
      checkForResponse();
    }*/

    /*if (!localClient.connected()) {
      disconnect_pachube();
    }
  }*/
}

void disconnect_pachube(){
  Serial.println("disconnecting.\n=====\n\n");
  localClient.stop();
  ready_to_update = false;
  reading_pachube = false;
  request_pause = true;
  last_connect = millis();
  found_content = false;
//  resetEthernetShield();
}


/*void checkForResponse(){  
  char c = localClient.read();
  //Serial.print(c);
  buff[pointer] = c;
  if (pointer < 64) pointer++;
  if (c == '\n') {
    found = strstr(buff, "200 OK");
    if (found != 0){
      found_status_200 = true; 
      //Serial.println("Status 200");
    }
    buff[pointer]=0;
    found_content = true;
    clean_buffer();    
  }

  if ((found_session_id) && (!found_CSV)){
    found = strstr(buff, "HTTP/1.1");
    if (found != 0){
      char csvLine[strlen(buff)-9];
      strncpy (csvLine,buff,strlen(buff)-9);

      //Serial.println("This is the retrieved CSV:");     
      //Serial.println("---");     
      //Serial.println(csvLine);
      //Serial.println("---");   
      Serial.println("\n--- updated: ");
      Serial.println(pachube_data);
      Serial.println("\n--- retrieved: ");
      char delims[] = ",";
      char *result = NULL;
      char * ptr;
      result = strtok_r( buff, delims, &ptr );
      int counter = 0;
      while( result != NULL ) {
        remoteSensor[counter++] = atof(result); 
        result = strtok_r( NULL, delims, &ptr );
      }  
      for (int i = 0; i < REMOTE_FEED_DATASTREAMS; i++){
        Serial.print( (int)remoteSensor[i]); // because we can't print floats
        Serial.print("\t");
      }

      found_CSV = true;

      Serial.print("\nsuccessful updates=");
      Serial.println(++successes);

    }
  }

  if (found_status_200){
    found = strstr(buff, "_id=");
    if (found != 0){
      clean_buffer();
      found_session_id = true; 
    }
  }
}*/



void setupEthernet(){
  resetEthernetShield();
  //Client remoteClient(255);
  delay(500);
  interval = UPDATE_INTERVAL;
  Serial.println("setup complete");
}

void clean_buffer() {
  pointer = 0;
  memset(buff,0,sizeof(buff)); 
}

void resetEthernetShield(){
  Serial.println("reset ethernet");
  Ethernet.begin(mac, ip);
}

